﻿using GrapeCity.Documents.Excel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using static System.Text.Json.JsonElement;

internal class JsonTable : ITableDataSource
{
    private readonly List<JsonElement> _jsonArray;
    private readonly Dictionary<int, string> _columnsMap;
    private readonly Dictionary<string, int> _reversedColumnsMap;

    public JsonTable(Stream inputStream)
    {
        string jsonContent;
        try
        {
            jsonContent = ConvertToString(inputStream);
        }
        catch (IOException e)
        {
            throw new Exception(e.Message, e);
        }

        _jsonArray = ToJsonArray(JsonDocument.Parse(jsonContent).RootElement.EnumerateArray());

        JsonElement jsonObject = _jsonArray[0];

        _columnsMap = new Dictionary<int, string>();
        _reversedColumnsMap = new Dictionary<string, int>();
        foreach (var property in jsonObject.EnumerateObject())
        {
            _columnsMap.Add(_columnsMap.Count, property.Name);
            _reversedColumnsMap.Add(property.Name, _reversedColumnsMap.Count);
        }
    }

    private List<JsonElement> ToJsonArray(ArrayEnumerator enumerator)
    {
        var jsonArray = new List<JsonElement>();
        while (enumerator.MoveNext())
        {
            jsonArray.Add(enumerator.Current);
        }
        return jsonArray;
    }

    private string ConvertToString(Stream inputStream)
    {
        using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8))
        {
            return reader.ReadToEnd();
        }
    }

    public int GetColumnCount()
    {
        return _columnsMap.Count;
    }

    public int GetColumnIndex(string columnName)
    {
        if (_reversedColumnsMap.TryGetValue(columnName, out int res))
        {
            return res;
        }

        return -1;
    }

    public string GetColumnName(int column)
    {
        if (_columnsMap.TryGetValue(column, out string res))
        {
            return res;
        }

        return null;
    }

    public int GetRowCount()
    {
        return _jsonArray.Count;
    }

    public object GetValue(int row, int column)
    {
        JsonElement jsonElement = _jsonArray[row].GetProperty(this.GetColumnName(column));

        switch (jsonElement.ValueKind)
        {
            case JsonValueKind.String:
                return jsonElement.GetString();
            case JsonValueKind.Number:
                return jsonElement.GetDouble();
            case JsonValueKind.True:
                return true;
            case JsonValueKind.False:
                return false;
            case JsonValueKind.Null:
            case JsonValueKind.Undefined:
            case JsonValueKind.Object:
            case JsonValueKind.Array:
            default:
                return null;
        }
    }
}